/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package vpkmanager;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InvalidClassException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author jquick
 */
public class VpkFile {
    
    public VpkFile(String filename) throws IOException {
        this.filename = filename;
        open();
    }
    
    private final String filename;
    private FileInputStream fileStream;
    public int treeLength;
    protected int magic = 0x55AA1234;
    protected int index;
    
    public int version;
    public int headerLength;
    protected List<VpkNode> nodes;
    
    public void open() throws FileNotFoundException, IOException {
            index = 0;
            fileStream = new FileInputStream(filename);
            BufferedInputStream reader = new BufferedInputStream(fileStream);
            load(reader);
            close();
    }
    
    void load(BufferedInputStream reader) throws InvalidClassException, IOException {
        int signature = readInt(reader);

        if (signature != magic)
            throw new InvalidClassException("Incorrect magic");

        version = readInt(reader);

        if (version < 1 || version > 2)
                throw new InvalidClassException("Unknown version");

        switch ((int)version) {
            case 1:
                loadVersion1Header(reader);
                break;
            case 2:
                loadVersion2Header(reader);
                break;
            default:
                throw new InvalidClassException("I got lost.");
        }
        this.nodes = loadRootNodes(reader); 
    }
    
    public List<VpkNode> loadRootNodes(BufferedInputStream reader) throws IOException {
        List<VpkNode> tmpnodes = new ArrayList<VpkNode>();

        VpkNode newNode = null;
        while (newNode == null || (newNode.name != null && !newNode.name.isEmpty())) {
            newNode = new VpkNode();
            index += newNode.load(reader, index);
            if (newNode.name != null && !newNode.name.isEmpty()) {
                  tmpnodes.add(newNode);
                  newNode.children = loadNodeChildren(reader, newNode);
            }
        }

        return tmpnodes;
    }
    
    public VpkNode[] loadNodeChildren(BufferedInputStream reader, VpkNode parent) throws IOException {
        List<VpkNode> tmpnodes = new ArrayList<VpkNode>();

        VpkNode newNode = null;
        while (newNode == null || (newNode.name != null && !newNode.name.isEmpty())) {
            newNode = new VpkNode(parent);
            index += newNode.load(reader, index);
            if (newNode.name != null && !newNode.name.isEmpty()) {
                tmpnodes.add(newNode);
                newNode.children = loadNodeFileChildren(reader, newNode);
            }
        }
        return tmpnodes.toArray(new VpkNode[tmpnodes.size()]);
    }
    
    public VpkNode[] loadNodeFileChildren(BufferedInputStream reader, VpkNode parent) throws IOException {
        List<VpkNode> tmpnodes = new ArrayList<VpkNode>();
        VpkNode newNode = null;
        
        while (newNode == null || (newNode.name != null && !newNode.name.isEmpty())) {
            newNode = new VpkNode(parent);
            index = newNode.loadFileInfo(reader, this);
            if (newNode.name != null && !newNode.name.isEmpty()) {
                tmpnodes.add(newNode);
            }
        }
        return tmpnodes.toArray(new VpkNode[tmpnodes.size()]);
    }
    
    void loadVersion1Header(BufferedInputStream reader) throws IOException {
            treeLength = readInt(reader);
            headerLength = 4;
    }

    void loadVersion2Header(BufferedInputStream reader) throws IOException {
            treeLength = readInt(reader);
            headerLength = 16;
            int unk1 = readInt(reader);
            int footerLength = readInt(reader);
            int unk2 = readInt(reader);
            int unk3 = readInt(reader);
    }
    
    public int dataOffset() throws InvalidClassException {
        switch ((int)version) {
            case 1:
                return 32 * 3;
            case 2:
                return 32 * 7;
            default:
                throw new InvalidClassException("Called DataOffset on a VpkFile with unknown version");
        }
    }
    
    public void close() throws IOException {
        if (fileStream != null) {
            fileStream.close();
            fileStream = null;
        }
    }
    
    public int readInt(BufferedInputStream reader) throws IOException {
        index += 4;
        byte[] barray = new byte[4];
        reader.read( barray, 0, 4 );
        ByteBuffer buffer = ByteBuffer.wrap(barray);
        buffer.order(ByteOrder.LITTLE_ENDIAN);
        return buffer.getInt();
    }
    
    public short readShort(BufferedInputStream reader) throws IOException {
        index += 2;
        byte[] barray = new byte[2];
        reader.read( barray, 0, 2 );
        ByteBuffer buffer = ByteBuffer.wrap(barray);
        buffer.order(ByteOrder.LITTLE_ENDIAN);
        return buffer.getShort();
    }
        
    public VpkNode getFile(String name) {
        for (VpkNode ext : nodes)
            for (VpkNode path : ext.children)
                for (VpkNode file : path.children)
                    if (file.getFilePath().equals(name))
                        return file;

        return null;               
    }
    /*
    public VpkNode[] GetAllFilesInDirectoryAndSubdirectories(string name) {
            var files = from node in nodes
                                    from dir in node.Children
                                    where dir.Name == name || dir.Name.StartsWith(name + "/")
                                    select dir.Children;
            return files.SelectMany(x => x).ToArray();
    }
    */
}
